#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sikle.h"

#define X(a, b) {#a, &b},
StacelType stacel_types[] = { STACEL_TYPES };
#undef X

void exec_gramel(SikleState* ss, unsigned int pc, int in_word_pool)
{
  GramelPool* gp = NULL;
  StringPool* sp = ss->string_pool;
  Gramel* gramel = NULL;

  gp = in_word_pool ? ss->word_pool : ss->gramel_pool;

  gramel = GRAMEL(gp->base);

  while(1) {
    StacelType st;

    switch(GRAMEL(gp->base+pc)->type) {
    case GT_INTEGER: {
      GInteger* ginteger = GINTEGER(gp->base+pc);
      SInteger sinteger;

      sinteger.type = STACEL_INTEGER;
      sinteger.value = ginteger->value;

      st = stacel_types[STACEL_INTEGER];
      st.operation->push(ss, STACEL(&sinteger));

      pc += sizeof(GInteger);
      break;
    }
    case GT_STRING: {
      GString* gstring = GSTRING(gp->base+pc);
      SString sstring;

      sstring.type = STACEL_STRING;
      sstring.value = gstring->sps;

      st = stacel_types[STACEL_STRING];
      st.operation->push(ss, STACEL(&sstring));
      pc += sizeof(GString);
      break;
    }
      /* call directly */
    case GT_BUILTIN_WORD: {
      GBuiltinWord* gword = GBUILTINWORD(gp->base+pc);
      Word* word;
      NString name;

      name = NString_new(NULL,0);
      NString_set(&name, sp->base+gword->name.offset, gword->name.size);
      word = Dictionary_get_word(ss->dictionary, name);

      if(word == NULL) {
	char* s = (char*)malloc(name.size+1);
	strncpy(s, name.data, name.size);
	s[name.size] = 0;
	printf("ERROR: word %s not defined\n", s);
	exit(0);
      }

      word->u.func(ss);

      pc += sizeof(GBuiltinWord);
      break;
    }
      /* call directly */
    case GT_WORD: {
      GWord* word = GWORD(gp->base+pc);
      unsigned int entry = word->entry;

      exec_gramel(ss, entry, TRUE);
      pc += sizeof(GWord);
      break;
    }
    case GT_ANONYMOUS_WORD: {
      GAnonymousWord* gword = GANONYMOUSWORD(gp->base+pc);
      SAnonymousWord sword;

      sword.type = STACEL_ANONYMOUS_WORD;
      sword.entry = gword->entry;

      st = stacel_types[STACEL_ANONYMOUS_WORD];
      st.operation->push(ss, STACEL(&sword));
      pc += sizeof(GAnonymousWord);
      break;
    }
    case GT_END: {
      return;
    }
    }
  }
}

void exec_gramel_main(SikleState* ss)
{
  exec_gramel(ss, 0, FALSE);
}

ExecStack* ExecStack_new(unsigned int size)
{
  ExecStack* es = (ExecStack*)malloc(sizeof(ExecStack));

  es->size = size;
  es->base = malloc(size);
  es->sp = 0;

  //  printf("esbase: %p\n", es->base);
  return es;
}

void ExecStack_dump(SikleState* ss)
{

}
